package com.aozhi.aipower.operation.entity.typehandler;

import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.invoker.Invoker;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Objects;

/**
 * 自定义枚举属性转换器
 *
 * @author hubin
 * @since 2017-10-11
 */
public class MybatisEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

  private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
  private final Class<E> type;

  private final Invoker invoker;

  public MybatisEnumTypeHandler(Class<E> type) {
    if (type == null) {
      throw new IllegalArgumentException("Type argument cannot be null");
    }
    this.type = type;
    MetaClass metaClass = MetaClass.forClass(type, REFLECTOR_FACTORY);
    String name = "value";
    this.invoker = metaClass.getGetInvoker(name);
  }

  @SuppressWarnings("Duplicates")
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType)
      throws SQLException {
    if (jdbcType == null) {
      ps.setObject(i, this.getValue(parameter));
    } else {
      // see r3589
      ps.setObject(i, this.getValue(parameter), jdbcType.TYPE_CODE);
    }
  }

  @Override
  public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
    if (null == rs.getObject(columnName) && rs.wasNull()) {
      return null;
    }
    return this.valueOf(this.type, rs.getObject(columnName));
  }

  @Override
  public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    if (null == rs.getObject(columnIndex) && rs.wasNull()) {
      return null;
    }
    return this.valueOf(this.type, rs.getObject(columnIndex));
  }

  @Override
  public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    if (null == cs.getObject(columnIndex) && cs.wasNull()) {
      return null;
    }
    return this.valueOf(this.type, cs.getObject(columnIndex));
  }

  private E valueOf(Class<E> enumClass, Object value) {
    E[] es = enumClass.getEnumConstants();
    return Arrays.stream(es).filter((e) -> equalsValue(value, getValue(e))).findAny().orElse(null);
  }

  /**
   * 值比较
   *
   * @param sourceValue 数据库字段值
   * @param targetValue 当前枚举属性值
   * @return 是否匹配
   * @since 3.3.0
   */
  protected boolean equalsValue(Object sourceValue, Object targetValue) {
    String sValue = StrUtil.trim(String.valueOf(sourceValue));
    String tValue = StrUtil.trim(String.valueOf(targetValue));
    if (sourceValue instanceof Number && targetValue instanceof Number
        && new BigDecimal(sValue).compareTo(new BigDecimal(tValue)) == 0) {
      return true;
    }
    return Objects.equals(sValue, tValue);
  }

  private Object getValue(Object object) {
    try {
      return invoker.invoke(object, new Object[0]);
    } catch (ReflectiveOperationException e) {
      throw new RuntimeException("");
    }
  }
}
